#include "doorbell_codec.h"
#include <sdkconfig.h>
#include <esp_codec_dev.h>
#include <esp_codec_dev_defaults.h>
#include <freertos/task.h>
#include <esp_err.h>
#include <driver/i2c.h>

#define BUF_LEN 2048

static esp_codec_dev_handle_t codec_dev = NULL;
static RingbufHandle_t mic_rb = NULL;
static RingbufHandle_t speaker_rb = NULL;
static TaskHandle_t mic_task = NULL;
static TaskHandle_t speaker_task = NULL;
static bool is_running = false;

static void doorbell_i2c_init(void)
{
    i2c_config_t i2c_cfg = {
        .mode = I2C_MODE_MASTER,
        .sda_pullup_en = GPIO_PULLUP_ENABLE,
        .scl_pullup_en = GPIO_PULLUP_ENABLE,
        .master.clk_speed = 100000,
        .sda_io_num = I2C_SDA_PIN,
        .scl_io_num = I2C_SCL_PIN,
    };
    ESP_ERROR_CHECK(i2c_param_config(I2C_PORT, &i2c_cfg));
    ESP_ERROR_CHECK(i2c_driver_install(I2C_PORT, i2c_cfg.mode, 0, 0, 0));
}

static void doorbell_i2s_init(i2s_chan_handle_t *rx_handle_p, i2s_chan_handle_t *tx_handle_p)
{
    i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_PORT, I2S_ROLE_MASTER);
    chan_cfg.auto_clear = true;
    ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, tx_handle_p, rx_handle_p));

    i2s_std_config_t std_cfg = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(I2S_SAMPLE_RATE),
        .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_BITS_PER_SAMPLE, I2S_CHANNEL_NUM),
        .gpio_cfg = {
            .bclk = I2S_BCK_PIN,
            .ws = I2S_WS_PIN,
            .dout = I2S_DO_PIN,
            .din = I2S_DI_PIN,
            .mclk = I2S_MCK_PIN,
        },
    };
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(*rx_handle_p, &std_cfg));
    ESP_ERROR_CHECK(i2s_channel_init_std_mode(*tx_handle_p, &std_cfg));
    ESP_ERROR_CHECK(i2s_channel_enable(*rx_handle_p));
    ESP_ERROR_CHECK(i2s_channel_enable(*tx_handle_p));
}

void doorbell_codec_init(RingbufHandle_t mic_buf, RingbufHandle_t speaker_buf)
{
    mic_rb = mic_buf;
    speaker_rb = speaker_buf;

    // 尝试初始化esp_codec_dev
    // 首先要初始化codec_if和data_if
    // 初始化data_if，需要首先初始化i2s
    i2s_chan_handle_t rx_handle = NULL, tx_handle = NULL;
    doorbell_i2s_init(&rx_handle, &tx_handle);
    // 初始化data_if
    audio_codec_i2s_cfg_t i2s_cfg = {
        .port = I2S_PORT,
        .rx_handle = rx_handle,
        .tx_handle = tx_handle,
    };
    const audio_codec_data_if_t *data_if = audio_codec_new_i2s_data(&i2s_cfg);
    assert(data_if);

    // 初始化codec_if
    doorbell_i2c_init();
    audio_codec_i2c_cfg_t i2c_cfg = {
        .port = I2C_PORT,
        .addr = ES8311_CODEC_DEFAULT_ADDR,
    };
    const audio_codec_gpio_if_t *gpio_if = audio_codec_new_gpio();
    assert(gpio_if);
    const audio_codec_ctrl_if_t *ctrl_if = audio_codec_new_i2c_ctrl(&i2c_cfg);
    assert(ctrl_if);
    es8311_codec_cfg_t es8311_cfg = {
        .ctrl_if = ctrl_if,
        .gpio_if = gpio_if,
        .codec_mode = ESP_CODEC_DEV_WORK_MODE_BOTH,
        .pa_pin = PA_PIN,
        .pa_reverted = false,
        .master_mode = false,
        .use_mclk = true,
        .mclk_div = I2S_MCLK_MULTIPLE_256,
        .digital_mic = false,
        .invert_mclk = false,
        .invert_sclk = false,
        .no_dac_ref = false,
    };
    const audio_codec_if_t *codec_if = es8311_codec_new(&es8311_cfg);
    assert(codec_if);

    esp_codec_dev_cfg_t codec_cfg = {
        .dev_type = ESP_CODEC_DEV_TYPE_IN_OUT,
        .codec_if = codec_if,
        .data_if = data_if,
    };
    codec_dev = esp_codec_dev_new(&codec_cfg);
    assert(codec_dev);
}

/* 将mic的数据不断写入mic_buf */
static void doorbell_mic_task(void *arg)
{
    void *buf = NULL;
    while (is_running)
    {
        assert(xRingbufferSendAcquire(mic_rb, &buf, BUF_LEN, portMAX_DELAY) == pdTRUE);
        ESP_ERROR_CHECK(esp_codec_dev_read(codec_dev, buf, BUF_LEN));
        assert(xRingbufferSendComplete(mic_rb, buf) == pdTRUE);
    }
    vTaskDelete(NULL);
}

/* 将speaker_buf的数据不断写入speaker */
static void doorbell_speaker_task(void *arg)
{
    void *buf = NULL;
    size_t buf_len = 0;
    while (is_running)
    {
        buf = xRingbufferReceive(speaker_rb, &buf_len, portMAX_DELAY);
        if (buf)
        {
            ESP_ERROR_CHECK(esp_codec_dev_write(codec_dev, buf, (int)buf_len));
            vRingbufferReturnItem(speaker_rb, buf);
        }
    }
    vTaskDelete(NULL);
}

void doorbell_codec_start(void)
{
    // 打开codec_dev
    esp_codec_dev_sample_info_t fs = {
        .bits_per_sample = I2S_BITS_PER_SAMPLE,
        .channel = I2S_CHANNEL_NUM,
        .sample_rate = I2S_SAMPLE_RATE,
        .channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0),
        .mclk_multiple = I2S_MCLK_MULTIPLE_256,
    };
    ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(codec_dev, 60.0));
    ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(codec_dev, 30.0));
    ESP_ERROR_CHECK(esp_codec_dev_open(codec_dev, &fs));

    is_running = true;

    // 开启后台任务
    xTaskCreate(doorbell_mic_task, "mic_task", 4096, NULL, 5, &mic_task);
    xTaskCreate(doorbell_speaker_task, "speaker_task", 4096, NULL, 5, &speaker_task);
}

void doorbell_codec_stop(void)
{
    is_running = false;
}

void doorbell_codec_play(uint8_t *data, size_t size)
{
    esp_codec_dev_write(codec_dev, data, (int)size);
}

void doorbell_codec_deinit(void)
{
    esp_codec_dev_delete(codec_dev);
}
